package reaction

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/cachex"
	"mall/common/ctxdatax"
	"mall/common/globalkey"
	"mall/common/mqueuex"
	"mall/common/resultx"
	"mall/service/attachment/rpc/attachmentclient"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/api/internal/lib/forumreaction"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/rpc/forumclient"
	"mall/service/user/rpc/userclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

const (
	batcherSize     = 100
	batcherBuffer   = 100
	batcherWorker   = 10
	batcherInterval = time.Second * 10
)

type CreateReactionLogic struct {
	logx.Logger
	ctx     context.Context
	svcCtx  *svc.ServiceContext
	batcher *mqueuex.Batcher
}

func NewCreateReactionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateReactionLogic {
	l := &CreateReactionLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}

	b := mqueuex.NewBatcher(
		mqueuex.WithSize(batcherSize),
		mqueuex.WithBuffer(batcherBuffer),
		mqueuex.WithWorker(batcherWorker),
		mqueuex.WithInterval(batcherInterval),
	)
	b.Sharding = func(key string) int {
		if i, err := strconv.ParseInt(key, 10, 64); err == nil {
			return int(i) % batcherWorker
		}

		return 0
	}
	b.Do = func(ctx context.Context, val map[string][]any) {
		var msgs []*mqueuex.ForumReactionMessage
		for _, vs := range val {
			for _, v := range vs {
				msgs = append(msgs, v.(*mqueuex.ForumReactionMessage))
			}
		}
		bs, err := json.Marshal(msgs)
		if err != nil {
			logrus.Info(fmt.Sprintf("CreateReaction Batcher.Do json.Marshal msgs: %v, error: %v", msgs, err))
		} else {
			if err = l.svcCtx.KqClient.Push(string(bs)); err != nil {
				logrus.Info(fmt.Sprintf("CreateReaction KqClient.Push msgs: %s, error: %v", string(bs), err))
			}
		}
	}
	l.batcher = b
	l.batcher.Start()

	return l
}

func (l *CreateReactionLogic) CreateReaction(req *types.CreateReactionReq, ip string) (resp *types.BaseDataInfo, err error) {
	if req.AuthorId == nil && req.TopicId == nil && req.PostId == nil && req.CommentId == nil {
		return nil, resultx.NewErrCode(resultx.FORUM_REACTION_CONTENT_ID_INVALID)
	}

	userId := ctxdatax.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	var redisKey string
	var contentId uint64
	if req.Type != nil { // 指定且为 "upvote"
		switch *req.Type {
		case "upvote":
			if req.CommentId != nil && *req.CommentId > 0 {
				redisKey = cachex.ForumCommentUpvote
				contentId = *req.CommentId

				forumReaction := forumreaction.NewForumReaction(l.ctx, l.svcCtx, redisKey)
				count, err := forumReaction.Do(strconv.FormatInt(int64(contentId), 10), strconv.FormatInt(int64(userId), 10))
				if err != nil {
					logrus.Info(fmt.Sprintf("CreateReaction upvote error : %+v ", err))

					return &types.BaseDataInfo{
						Status:  "fail",
						Message: "点赞失败",
					}, nil
				}

				if count >= 0 {
					msg := &mqueuex.ForumReactionMessage{
						AuthorId:  req.AuthorId,
						TopicId:   req.TopicId,
						PostId:    req.PostId,
						CommentId: req.CommentId,
						UserId:    userId,
						Type:      redisKey,
						Status:    1,
					}
					if err = l.batcher.Add(strconv.FormatInt(int64(userId), 10), msg); err != nil {
						logrus.Info(fmt.Sprintf("CreateReaction upvote batcher.Add error : %+v , message : %+v ", err, msg))
					}

					return &types.BaseDataInfo{
						Status:  "ok",
						Message: "点赞成功",
						Count:   count,
					}, nil
				}

				logrus.Info(fmt.Sprintf("CreateReaction upvote failed : %+v ", count))

				return &types.BaseDataInfo{
					Status:  "fail",
					Message: "点赞失败",
				}, nil
			}
		}
	} else { // 默认为 "like"
		if req.TopicId != nil && *req.TopicId > 0 {
			if req.PostId != nil && *req.PostId > 0 {
				redisKey = cachex.ForumAnswerLike
				contentId = *req.PostId
			} else {
				redisKey = cachex.ForumQuestionLike
				contentId = *req.TopicId
			}
		} else if req.PostId != nil && *req.PostId > 0 {
			redisKey = cachex.ForumReviewLike
			contentId = *req.PostId
		}

		forumReaction := forumreaction.NewForumReaction(l.ctx, l.svcCtx, redisKey)
		count, err := forumReaction.Do(strconv.FormatInt(int64(contentId), 10), strconv.FormatInt(int64(userId), 10))
		if err != nil {
			logrus.Info(fmt.Sprintf("CreateReaction like error : %+v ", err))

			return &types.BaseDataInfo{
				Status:  "fail",
				Message: "点赞失败",
			}, nil
		}

		if count >= 0 {
			msg := &mqueuex.ForumReactionMessage{
				AuthorId: req.AuthorId,
				TopicId:  req.TopicId,
				PostId:   req.PostId,
				UserId:   userId,
				Type:     redisKey,
				Status:   1,
			}
			if err = l.batcher.Add(strconv.FormatInt(int64(userId), 10), msg); err != nil {
				logrus.Info(fmt.Sprintf("CreateReaction like batcher.Add error : %+v , message : %+v ", err, msg))
			}

			return &types.BaseDataInfo{
				Status:  "ok",
				Message: "点赞成功",
				Count:   count,
			}, nil
		}

		logrus.Info(fmt.Sprintf("CreateReaction like failed : %+v ", count))

		return &types.BaseDataInfo{
			Status:  "fail",
			Message: "点赞失败",
		}, nil
	}

	return l.createReport(req, ip, userId)
}

func (l *CreateReactionLogic) createReport(req *types.CreateReactionReq, ip string, userId uint64) (resp *types.BaseDataInfo, err error) {
	userInfo, err := l.svcCtx.UserRpc.GetUserAuthByUserId(l.ctx, &userclient.GetUserAuthByUserIdReq{
		PlatformId: pointy.GetPointer(globalkey.ThirdPartyPlatformWechatMini),
		UserId:     pointy.GetPointer(userId),
		WithRank:   pointy.GetPointer(uint32(1)),
	})
	if err != nil {
		logx.Infow("CreateReaction UserRpc.GetUserAuthByUserId err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	if userInfo.Openid == nil {
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	author := &forumclient.ForumAuthorInfo{
		Id:     userInfo.UserId,
		Name:   userInfo.Nickname,
		Avatar: userInfo.Avatar,
		Ip:     pointy.GetPointer(ip),
		Level:  userInfo.RankId,
	}

	newThumbMap := make(map[string]*types.MediaInfo)
	var newMedia []*forumclient.ForumMediaInfo
	var newMediaIds []string
	if len(req.Photos) > 0 {
		for idx, v := range req.Photos {
			if v.Order == nil {
				v.Order = pointy.GetPointer(uint32(idx))
			}
			if v.Id != nil && len(*v.Id) > 0 {
				if _, ok := newThumbMap[*v.Id]; !ok { // 忽略重复id
					newThumbMap[*v.Id] = v
					newMediaIds = append(newMediaIds, *v.Id)
				}
			} else {
				var thumbnail *uint32
				if v.Thumbnail != nil && *v.Thumbnail {
					thumbnail = pointy.GetPointer(uint32(1))
				}
				newMedia = append(newMedia, &forumclient.ForumMediaInfo{ // 第三方图片
					Uid:       v.Uid,
					Url:       v.Url,
					Thumbnail: thumbnail,
					AltText:   v.AltText,
					Meta:      v.Details,
					Order:     v.Order,
				})
			}
		}
	}

	// 根据 newMediaIds 查询 attachments 记录到 forum_attachment_relationships
	if len(newMediaIds) > 0 {
		res, err := l.svcCtx.AttachmentRpc.BackendGetFileList(l.ctx, &attachmentclient.FileListReq{
			Ids: pointy.GetPointer(strings.Join(newMediaIds, ",")),
		})
		if err != nil {
			return nil, err
		}

		if len(res.List) > 0 {
			for _, v := range res.List {
				var url *string
				if v.Url != nil && len(*v.Url) > 0 {
					url = pointy.GetPointer(fmt.Sprintf("%s%s", l.svcCtx.Config.NfsConf.StaticUrl, *v.Url))
				}
				if url == nil && v.Path != nil && len(*v.Path) > 0 {
					url = pointy.GetPointer(fmt.Sprintf("%s%s", l.svcCtx.Config.NfsConf.ProxyUrl, *v.Path))
				}
				if media, ok := newThumbMap[*v.Id]; ok {
					var thumbnail *uint32
					if media.Thumbnail != nil && *media.Thumbnail {
						thumbnail = pointy.GetPointer(uint32(1))
					}
					newMedia = append(newMedia, &forumclient.ForumMediaInfo{
						Uid:       media.Uid,
						Id:        v.Id,
						Uri:       media.Url,
						Url:       url,
						Thumbnail: thumbnail,
						AltText:   media.AltText,
						Meta:      media.Details,
						Order:     media.Order,
					})
				}
			}
		}
	}

	var meta []*forumclient.ForumMetaInfo
	if len(req.Meta) > 0 {
		for _, v := range req.Meta {
			if v.Key == nil || v.Value == nil {
				continue
			}

			if len(strings.TrimSpace(*v.Key)) == 0 || len(strings.TrimSpace(*v.Value)) == 0 {
				continue
			}

			meta = append(meta, &forumclient.ForumMetaInfo{
				Id:    v.Id,
				Key:   v.Key,
				Value: v.Value,
			})
		}
	}

	var content *string
	if req.Content != nil && len(strings.TrimSpace(*req.Content)) > 0 {
		content = pointy.GetPointer(strings.TrimSpace(*req.Content))
	}

	_, err = l.svcCtx.ForumRpc.CreateReaction(l.ctx, &forumclient.ReactionInfo{
		AuthorId:    req.AuthorId, // 作者ID，不同于用户ID
		TopicId:     req.TopicId,
		PostId:      req.PostId,
		CommentId:   req.CommentId,
		Type:        req.Type,
		ContentHtml: content,
		Author:      author, // reaction 用户信息
		NewMedia:    newMedia,
		Meta:        meta,
	})
	if err != nil {
		return nil, err
	}

	return &types.BaseDataInfo{
		Status:  "ok",
		Message: "操作成功",
	}, nil
}
